﻿using System;
using UnityEngine;
using System.Collections;
using System.Collections.Generic;
using System.IO;
using Newtonsoft.Json;

namespace Inlycat.Log
{

    /// <summary>
    /// Author:gouzhun 
    /// Manager for logging, contains logging to file, logging to console, logging to screen function.
    /// Generally called by PLog, do not recmmend to directly use this class
    /// </summary>
    public class LogManager : Singleton<LogManager>
    {
        private static string LogConfigPath = Application.persistentDataPath + "/log_config.txt";
        private static string DefaultDev = "DefaultDev";
        private static string DefaultModule = "DefaultModule";

        public string FormatVerbose;
        public string FormatInfo;
        public string FormatWarn;
        public string FormatErr;
        public string FormatEx;
        public string FormatFile;

        private bool _openUpload = false;
        private int _uploadLength = -1;

        private LogConfig _config;

        private string _logPath;
        private string LogPath
        {
            get
            {
                if (string.IsNullOrEmpty(_logPath))
                {
                    _logPath = Application.persistentDataPath + "/app_log/";
                    if (!Directory.Exists(_logPath))
                    {
                        Directory.CreateDirectory(_logPath);
                        Debug.Log("Create path:" + _logPath);
                    }
                }

                return _logPath;
            }
        }

        private string FileName
        {
            get { return System.DateTime.Now.ToShortDateString().Replace("/", "_") + ".log"; }
        }

        private string FilePath
        {
            get { return Path.Combine(LogPath, FileName); }
        }

        public bool IsEnable
        {
            get { return _config.IsEnable; }
        }

        public bool OpenUpload
        {
            get { return _openUpload; }
        }

        public int UploadLength
        {
            get { return _uploadLength; }
        }


        private LogManager()
        {
            _config = ReadConfig();
            if (_config == null)
            {
                _config = new LogConfig()
                {
                    IsEnable = true,
                    IsLogFileEnable = true,
                    IsLogConsoleEnable = true,
                    DevDic = new Dictionary<string, bool>(),
                    ModuleDic = new Dictionary<string, bool>(),
                };
            }

            CheckAndAddKey(ref _config.DevDic, DefaultDev);

            CheckAndAddKey(ref _config.ModuleDic, DefaultModule);

            FormatVerbose = "I [{0}]: {1}";
            FormatInfo = "I [{0}]: {1}";
            FormatWarn = "W [{0}]: {1}";
            FormatErr = "Err [{0}]: {1}";
            FormatEx = "Ex [{0}]: {1} - Message: {2}  StackTrace: {3}";
            FormatFile = "\n{0}{1}\t{2}\n";

#if disablelog
        _config.IsEnable = false;
#endif

#if disablelogfile
        _config.IsLogFileEnable = false;
#endif

#if disablelogconsole
        _config.IsLogConsoleEnable = false;
#endif
        }



        /// <summary>
        /// log message with developer name and module name
        /// </summary>
        /// <param name="msg">the message to log</param>
        /// <param name="devName">developer name, default value is null</param>
        /// <param name="moduleName">module name,default value is null</param>
        /// <param name="level">log level, might be Loglevels.All etc. </param>
        public void Log(string msg, string devName = null, string moduleName = null, Loglevels level = Loglevels.All)
        {
            if (!_config.IsEnable)
            {
                return;
            }

            if (string.IsNullOrEmpty(devName))
            {
                devName = DefaultDev;
            }

            if (string.IsNullOrEmpty(moduleName))
            {
                moduleName = DefaultModule;
            }

            var devEnable = CheckAndAddKey(ref _config.DevDic, devName) && _config.DevDic[devName];
            var moduleEnable = CheckAndAddKey(ref _config.ModuleDic, moduleName) && _config.ModuleDic[moduleName];

            if (devEnable && moduleEnable && _config.Level <= level)
            {
                // log to console
                if (_config.IsLogConsoleEnable)
                {
                    switch (level)
                    {
                        case Loglevels.All:
                            UnityEngine.Debug.Log(string.Format(FormatVerbose, moduleName, msg));
                            break;
                        case Loglevels.Information:
                            UnityEngine.Debug.Log(string.Format(FormatInfo, moduleName, msg));
                            break;
                        case Loglevels.Warning:
                            UnityEngine.Debug.Log(string.Format(FormatWarn, moduleName, msg));
                            break;
                        case Loglevels.Error:
                            UnityEngine.Debug.Log(string.Format(FormatErr, moduleName, msg));
                            break;
                        case Loglevels.Exception:
                            UnityEngine.Debug.Log(string.Format(FormatEx, moduleName, msg));
                            break;
                        case Loglevels.None:

                            break;
                    }
                }

                // log to file
                if (_config.IsLogFileEnable)
                {
                    try
                    {
                        var content = string.Format(FormatFile, DateTime.Now.ToShortTimeString().Replace("/", "_"), ":" + level.ToString(), msg);
                        if (File.Exists(FilePath))
                        {
                            File.AppendAllText(FilePath, content);
                        }
                        else
                        {
                            File.WriteAllText(FilePath, content);
                        }
                    }
                    catch (Exception e)
                    {
                        Debug.Log(string.Format("Write to log failed :{0}\t EXCEPTION：{1}", FilePath, e.Message));
                    }
                }
            }
        }

        public void UploadLog(int id, string url, string filePath, int length)
        {
            if (!OpenUpload || !File.Exists(filePath))
            {
                return;
            }
            WWWForm form = new WWWForm();
            string s = File.ReadAllText(filePath);
            if (length > 0 && s.Length > length)
            {
                s = s.Substring(s.Length - length);
            }
            form.AddField("id", id);
            form.AddField("log", s);
            WWW www = new WWW(url, form);
            CoroutineManager.Instance.DoAction(upload(www));
        }

        private IEnumerator upload(WWW www)
        {
            yield return www;
            Debug.Log(www.text);
        }

        public void GetUploadInfo(int id, string url)
        {
            WWWForm form = new WWWForm();
            form.AddField("id", id);
            WWW www = new WWW(url, form);
            CoroutineManager.Instance.DoAction(saveInfo(www));
        }

        private IEnumerator saveInfo(WWW www)
        {
            yield return www;
            Debug.Log(www.text);
            string s = www.text;
            string[] a = s.Split(',');
            _openUpload = a[0] == "true";
            _uploadLength = int.Parse(a[1]);
        }

        #region private methods

        private void LogConsole()
        {

        }

        private LogConfig ReadConfig()
        {
            try
            {
                if (File.Exists(LogConfigPath))
                {
                    var content = File.ReadAllText(LogConfigPath);
                    var config = JsonConvert.DeserializeObject<LogConfig>(content);
                    return config;
                }
            }
            catch (Exception)
            {
                return null;
            }
            return null;
        }

        private bool CheckAndAddKey(ref Dictionary<string, bool> dic, string key)
        {
            if (dic.ContainsKey(key))
            {
                return true;
            }
            dic[key] = true;
            SyncConfigFile();
            return false;
        }



        #endregion

        public void SyncConfigFile()
        {
            var content = JsonConvert.SerializeObject(_config);
            File.WriteAllText(LogConfigPath, content);
        }

        public LogConfig GetConfig()
        {
            return _config;
        }
    }

    public class LogConfig
    {
        public bool IsEnable;

        public bool IsLogFileEnable;

        public bool IsLogConsoleEnable;

        public bool IsLogScreenEnable;

        public Dictionary<string, bool> DevDic;

        public Dictionary<string, bool> ModuleDic;

        public Loglevels Level;
    }

    /// <summary>
    /// Available logging levels.
    /// </summary>
    public enum Loglevels
    {
        /// <summary>
        /// All message will be logged.
        /// </summary>
        All,

        /// <summary>
        /// Only Informations and above will be logged.
        /// </summary>
        Information,

        /// <summary>
        /// Only Warnings and above will be logged.
        /// </summary>
        Warning,

        /// <summary>
        /// Only Errors and above will be logged.
        /// </summary>
        Error,

        /// <summary>
        /// Only Exceptions will be logged.
        /// </summary>
        Exception,

        /// <summary>
        /// No logging will be occur.
        /// </summary>
        None
    }
}